// author diqye 262666212@qq.com
(function(exports,fn){
  exports.declare = fn()
}(this,function(){

  function _1(selector,div){
    return div.querySelectorAll(selector)
  }
  function partial(args,fn){
    return function partial_(){
      var nargs = [].slice.call(arguments)
      return fn.apply(this,args.concat(nargs))
    }
  }
  function curryN(n,fn){
    var r = function curryN_(){
      if(n==0){
        return fn()
      }else{
        var args = [].slice.call(arguments)
        if(args.length >= n){
          return fn.apply(null,args);
        }else{
          return curryN(n-args.length,partial(args,fn))
        }
      }
    }
    r.toString=function(){
      return 'curry: 还缺少 '+n+'个参数'
    }
    return r
  }
  function curry(fn){
    return curryN(fn.length, fn)
  }
  function each(fn, arr){
    for(var i=0;i<arr.length;i++){
      fn(arr[i])
    }
  }
  function isPromise(a){
    return Object.prototype.toString.call(a) == "[object Promise]"
  }
  function head(arr){
    return arr[0]
  }
  function tail(arr){
    return arr.slice(1)
  }
  function isEmpty(arr){
    return arr.length == 0
  }
  function applyFns(fns,args){
    var fn = head(fns)
    var tfn = tail(fns)
    var pr = fn.apply(null,args)
    if(isPromise(pr)){
      if(isEmpty(tfn)){
        return pr
      }else{
        return pr.then(function(data){
          var r = applyFns(tfn,[data])
          return r
        })
      }
    }else{
      if(isEmpty(tfn)){
        return pr
      }else{
        return applyFns(tfn,[pr])
      }
    }
  }
  function pipe(){
    var fns = [].slice.call(arguments)
    return function(){
      var nargs = [].slice.call(arguments)
      return applyFns(fns,nargs)
    }
  }
  function _(){
    var args = [].slice.call(arguments)
    var start = head(args)
    var fns = tail(args)
    var fn = pipe.apply(null,fns)
    return fn(start)
  }
  function merge(obj,nobj){
    var r = {}
    for(var key in obj){
      r[key] = obj[key]
    }
    for(var key in nobj){
      r[key] = nobj[key]
    }
    return r
  }
  function findParentScope(el){
    if(el){
      if(el.tagName == 'BODY' || el.tagName == 'HTML'){
        return null
      }else if(el.attributes&&el.attributes.getNamedItem('d-scope')){
        return el
      }else{
        return findParentScope(el.parentElement)
      }
    }else{
      return null
    }
  }
  function _8(a,$el){
    return _1(a,document.body)
  }
  function isElement(node){
    return node.nodeType == 1
  }

  function attr(name,el){
    if(el&&el.attributes){
      var item = el.attributes.getNamedItem(name)
      if(item){
        return item.value
      }else{
        return null
      }
    }else{
      return null
    }
  }
  var hasAttr = curry(function(name,el){
    if(el&&el.attributes){
        return el.attributes.getNamedItem(name) != null
      }else{
        return false
      }
  })
  function isArray(a){
    return Object.prototype.toString.call(a) === '[object Array]'
  }
  function disProxy(el,fn){
    return function(event){
      var v = attr('d-dis',el)
      if(v=='y'){
        void null
      }else{
        fn(event)
      }
    }
  }
 
  function createExpr(expr){
    var nexpr = parser(expr,function(str){
      return '$_7("'+str+'")'
    },function(str){
      return '$_8("'+str+'")'
    })
    return new Function('context','with(context){'+nexpr+'}')
  }
  function parser(code,selectfn,select2fn){
    var ncode = []
    var sType = []
    var seType = []
    var se2Type = []
    var ctype = "other" // other string selector dstring selector2
    for(var i=0;i<code.length;i++){
      var siglestr = code.charAt(i)
      dofn(siglestr)
    }
    function dofn(siglestr){
      if(ctype === 'other'){
        if(siglestr === "'"){
          ctype = "string"
          sType = ["'"]
        }else if(siglestr == '"'){
          ctype = "dstring"
          sType = ["'"]
        }else if(siglestr==='@'){
          ctype = "selector"
          seType = []
        }else if(siglestr==='*'){
          ctype = "selector2"
          se2Type = []
        }else{
          ncode.push(siglestr)
        }
      }else if(ctype === 'string'){
        if(siglestr === "'"){
          ctype = "other"
          sType.push("'")
          ncode.push(sType.join(''))
          sType = []
        }else if(siglestr === ''){
          ncode.push(sType.join(''))
        }else{
          sType.push(siglestr)
        }
      }else if(ctype === 'dstring'){
        if(siglestr === '"'){
          ctype = "other"
          sType.push(siglestr)
          ncode.push(sType.join(''))
          sType = []
        }else if(siglestr === ''){
          ncode.push(sType.join(''))
        }else{
          sType.push(siglestr)
        }
      }else if(ctype === 'selector'){
        var endFlag = [',','(',')',"'",'"',';']
        if(endFlag.indexOf(siglestr) !== -1){
          endSelector()
          dofn(siglestr)
        }else if(siglestr === ''){
          endSelector()
        }else{
          seType.push(siglestr)
        }
      }else if(ctype === 'selector2'){
        var endFlag = [',','(',')',"'",'"',';']
        if(endFlag.indexOf(siglestr) !== -1){
          endSelector2()
          dofn(siglestr)
        }else if(siglestr === ''){
          endSelector2()
        }else{
          se2Type.push(siglestr)
        }
      }else{
        ncode.push(siglestr)
      }
    }
    function endSelector(){
      ctype = 'other'
      ncode.push(selectfn(seType.join('')))
      seType = []
    }
    function endSelector2(){
      ctype = 'other'
      ncode.push(select2fn(se2Type.join('')))
      se2Type = []
    }
    dofn('')
    return ncode.join('')
  }

  function addMOD(name,fn){
    if(MOD[name] != null){
      throw '插件名字('+name+')已经存在'
    }else{
      MOD[name] = fn
    }
  }

  function zip(a,b){
    return [a,b]
  }
  function zip1(a){
    return a[0]
  }
  function zip2(a){
    return a[1]
  }

  function sattr(name,val,els){
    each(function(el){
      el.setAttribute(name,val)
      
    },els)
    return els
  }
  
  function isBlank(str){
    return str.trim() == ''
  }
  function notBlank(str){
    return isBlank(str) == false
  }
  var split = curry(function split_(sep,str){
    return str.split(sep)
  })
  var filter = curry(function filter_(fn,arr){
    var r = []
    for(var i=0;i<arr.length;i++){
      if(fn(arr[i])){
        r.push(arr[i])
      }else{
        void null
      }
    }
    return r
  })
  var eq = curry(function eq_(a,b){
    return a == b
  })
  function complement(fn){
    return function(a){
      return fn(a) == false
    }
  }
  var aclass = curry(function aclass_(elClass,els){
    var doCls = pipe(split(' '),filter(notBlank),filter(complement(eq(elClass))))
    each(function(el){
      var clazz = el.className||''
      var clas = doCls(clazz)
      clas.push(elClass)
      el.className = clas.join(' ')
    },els)
    return els
  })
  var cclass = curry(function cclass_(els){
    each(function(el){
      el.className = ''
    },els)
    return els
  })
  var rclass = curry(function rclass_(elClass,els){
    var doCls = pipe(split(' '),filter(notBlank),filter(complement(eq(elClass))))

    each(function(el){
      var clazz = el.className||''
      var clas = doCls(clazz)
      el.className = clas.join(' ')
    },els)
    return els
  })
// ================== main logic ==========
  var MOD={
  }


  function dohandler(el,context,HANDLER){
    each(function(zipa){
      var test = zip1(zipa)
      if(test(el)){
        zip2(zipa)(context,el)
      }else{
        void null
      }
    },HANDLER)
  }
  function create(el,context,type){
    var context = merge(context,{
      $_7:function(selector){return _1(selector,context.$root)},
      $_8:_8,
      _:_,
      $root:context.$root?context.$root:el
    })
    if(isElement(el)){
      if(type == 'NewMod'){
        dofn()
      }else if(hasAttr('d-mod',el)){
        var mod = attr('d-mod',el)
        var mods = mod.split(' ')
        var cmod = context
        var $handlers = []
        if(context.$handlers){
          $handlers = context.$handlers.concat([])
        }else{
          void null
        }
        function addH(testfn,h){
          $handlers.push(zip(testfn,function(context,el){
            h(function(expr,e){
              var fn = createExpr(expr)
              fn(merge(context,{$e:e,$self:[el]}))
            },context,el)
          }))
        }
        each(function(omod){
          var mod = omod.trim()
          if(mod == ''){
            void null
          }else if(MOD[mod]==null){
            throw "mod " + mod +" 不存在"
          }else{
            var mod = MOD[mod](el,addH)
            if(mod == null){
              void null
            }else{
              cmod = merge(cmod,mod)
            }
          }
        },mods)
        cmod.$handlers = $handlers
        cmod.$root = el
        cmod.$return = function(fs){
          if(isArray(fs)){
            fs.push(cmod)
          }else{
            void null
          }
        }
        create(el,cmod,'NewMod')
      }else{
        dofn()
      }
    }else{
      void null
    }
    function dofn(){
      if(context.$handlers){
        dohandler(el,context,context.$handlers)
      }
      var len = el.childNodes.length
      for(var i=0;i<len;i++){
        var item = el.childNodes.item(i)
        create(item,context)
      }
    }

  }


  function main(){
    //添加自带模块
    addMOD('main',function(el,addHander){
      addHander(hasAttr('d-click'),function(exec,context, el){
        el.addEventListener('click',disProxy(el,function($e){
          var expr = attr('d-click',el)
          exec(expr,$e)
        }))
      })
      addHander(hasAttr('d-init'),function(exec,context,el){
        var expr = attr('d-init',el)
        exec(expr,null)
      })
      addHander(hasAttr('d-check'),function(exec,context,el){
        var expr = attr('d-check',el)
        el.addEventListener('change',function(e){
          if(el.checked){
            exec(expr,e)
          }else{
            void null
          }
        })
      })
      addHander(hasAttr('d-uncheck'),function(exec,context,el){
        var expr = attr('d-uncheck',el)
        el.addEventListener('change',function(e){
          if(el.checked){
            void null
          }else{
            exec(expr,e)
          }
        })
      })

      return {
        sattr:curry(sattr),
        rclass:rclass,
        aclass:aclass,
        cclass:cclass
      }
    })
    // create(document.body,{})


  }
  main()
  

  return {
    parser:parser,
    hasAttr:hasAttr,
    create:create,
    addMod:addMOD,
    hasAttr:hasAttr,
    pipe:pipe,
    _:_,
    curry:curry
  }
}))